#ifndef _PM_E_HASH_H
#define _PM_E_HASH_H

#include<cstdint>

#include<queue>
#include<map>
#include <unistd.h>
#include"data_page.h"
#include<iostream>
#define DATA "/home/wangyw/DBMS/data/" //页面表的存储位置为/mnt/pmemdir/，统一命名格式为filex，x为file的序号，如第一个file的名字为file1。在利用这个文件夹创建NVM文件时，需要先将/dev/pmem0挂载到/mnt/pmemdir上。
using namespace std;
#define META_NAME                                "pm_ehash_metadata"
#define CATALOG_NAME                        "pm_ehash_catalog"
#define PM_EHASH_DIRECTORY        "/home/wangyw/DBMS/data/"        // add your own directory path to store the pm_ehash

using std::queue;
using std::map;

/* 
---the physical address of data in NVM---
fileId: 1-N, the data page name
offset: data offset in the file
*/
typedef struct pm_address 
//存储一个bucket的RID，即页面表号数加页面内偏移量。当要访问一个bucket的时候，可以通过页面表号数访问到相应的文件，从而取出文件在内存中的地址，再将这个地址加上页面内偏移量，我们就能访问到内存中的一个bucket。
{
    uint32_t fileId;
    uint32_t offset;
} pm_address;


// in ehash_catalog, the virtual address of buckets_pm_address[n] is stored in buckets_virtual_address
// buckets_pm_address: open catalog file and store the virtual address of file
// buckets_virtual_address: store virtual address of bucket that each buckets_pm_address points to
typedef struct ehash_catalog
//哈希表目录文件定义。用bucket_pm_address数组存储所有存在的bucket的RID；用buckets_virtual_address存储所有存在的bucket的实际内存地址。
{
    pm_address* buckets_pm_address;         // pm address array of buckets
    pm_bucket** buckets_virtual_address;    // virtual address of buckets that buckets_pm_address point to
} ehash_catalog;

typedef struct ehash_metadata 
//用于运行时，显示我们使用中的各个页面表的相关信息。max_file_id指向下一个可以被分配的页面表号数。catalog_size表示当前页面表中存在的bucket的个数。
{
    uint64_t max_file_id;      // next file id that can be allocated
    uint64_t catalog_size;     // the catalog size of catalog file(amount of data entry)
    uint64_t global_depth;   // global depth of PmEHash
    data_page * data_page_addr; //address of data_page // 这是文宝自己加的？
} ehash_metadata;

class PmEHash
{
private:
    data_page ** page_list;
    int page_list_len;
    ehash_metadata*                               metadata;                    // virtual address of metadata, mapping the metadata file
    ehash_catalog                                     catalog;                        // the catalog of hash
    data_page** page; // for T version
    queue<pm_bucket*>                         free_list;                      //all free slots in data pages to store buckets 存放所有空bucket。页面表中的slot和bucket是一个意思。也就是说slot = bucket；
    map<pm_bucket*, pm_address> vAddr2pmAddr;       // map virtual address to pm_address, used to find specific pm_address 将bucket的实际内存地址和RID绑定起来，可以根据bucket的实际内存地址来找到这个bucket的RID；
    map<pm_address, pm_bucket*> pmAddr2vAddr;       // map pm_address to virtual address, used to find specific virtual address    将bucket的实际内存地址和RID绑定起来，可以根据bucket的RID来找到这个bucket的实际内存地址；
    
    uint64_t hashFunc(uint64_t key); //哈希函数定义

    pm_bucket* getFreeBucket(uint64_t key); //获得要插入的bucket，如果桶满了就利用splitBucket函数进行分裂桶操作，返回新桶的地址；
    pm_bucket* getNewBucket(); //在.cpp文件中没有定义。
    void freeEmptyBucket(pm_bucket* bucket);//在.cpp文件中没有定义；
    kv* getFreeKvSlot(pm_bucket* bucket); //在可供插入的桶中找到一个可以插入的位置，并返回；

    void splitBucket(uint64_t bucket_id); //分裂桶，调用getNewbucket()来获得一个空的bucket。
    void mergeBucket(uint64_t bucket_id); //回收桶；

    void extendCatalog();  //增加目录；
    void* getFreeSlot(pm_address& new_address); //得到一个空的bucket，如果free_list中存在一个可用的空的bucket的地址，那么就返回这个空的bucket的地址。如果free_list队列为空，那么我们使用allocNewPage()函数来创建一个新的页面表。然后再重新从free_list中拿出空bucket的地址。
    void allocNewPage(); //调用data_page.h中的newPage()函数，生成一个新的页面表，并将页面表中所有的bucket放入free_list中；

    void recover(); 
    void mapAllPage();


public:
    PmEHash();
    ~PmEHash();

    int insert(kv new_kv_pair);
    int remove(uint64_t key);
    int update(kv kv_pair);
    int search(uint64_t key, uint64_t& return_val);

    void selfDestory();
};

#endif
